/*
REWRITTEN FROM SCRATCH BY PUSSYWIZARD, IT OWNS NOW!
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "trial_of_the_crusader.h"
#include "Vehicle.h"
#include "Player.h"

/***********
** GORMOK
***********/

enum GormokSpells
{
	SPELL_IMPALE						= 66331,
	SPELL_STAGGERING_STOMP				= 67648,
	SPELL_RISING_ANGER					= 66636,
	SPELL_CHANGE_VEHICLE				= 66342, // custom spell
	//Snobold
	SPELL_SNOBOLLED						= 66406,
	SPELL_BATTER						= 66408,
	SPELL_FIRE_BOMB						= 66313,
	SPELL_FIRE_BOMB_AURA				= 66318,
	SPELL_HEAD_CRACK					= 66407,
};

enum GormokEvents
{
	EVENT_SPELL_IMPALE = 1,
	EVENT_SPELL_STAGGERING_STOMP,
	EVENT_PICK_SNOBOLD_TARGET,
	EVENT_RELEASE_SNOBOLD,

	EVENT_SPELL_SNOBOLLED,
	EVENT_SPELL_BATTER,
	EVENT_SPELL_FIRE_BOMB,
	EVENT_SPELL_HEAD_CRACK,
};

enum GormokNPCs
{
	NPC_SNOBOLD_VASSAL					= 34800,
	NPC_FIRE_BOMB						= 34854,
};

enum Yells
{
    // Gormok
    EMOTE_SNOBOLLED         = 0,

    // Acidmaw & Dreadscale
    EMOTE_ENRAGE            = 0,
	EMOTE_SUBMERGE          = 1,
	EMOTE_EMERGE            = 2,

    // Icehowl
    EMOTE_TRAMPLE_STARE     = 0,
    EMOTE_TRAMPLE_CRASH     = 1,
    EMOTE_TRAMPLE_FAIL      = 2,
};

class npc_snobold_vassal : public CreatureScript
{
public:
	npc_snobold_vassal() : CreatureScript("npc_snobold_vassal") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new npc_snobold_vassalAI(pCreature);
	}

	struct npc_snobold_vassalAI : public ScriptedAI
	{
		npc_snobold_vassalAI(Creature* pCreature) : ScriptedAI(pCreature)
		{
			pInstance = pCreature->GetInstanceScript();
			TargetGUID = 0;
			me->SetReactState(REACT_PASSIVE);
		}

		InstanceScript* pInstance;
		EventMap events;
		uint64 TargetGUID;

		void Reset()
		{
			events.Reset();
			events.ScheduleEvent(EVENT_SPELL_FIRE_BOMB, urand(10000,30000));
		}

		void EnterCombat(Unit* who)
		{
			events.Reset();
			events.ScheduleEvent(EVENT_SPELL_SNOBOLLED, 1500);
			events.ScheduleEvent(EVENT_SPELL_BATTER, 5000);
			events.ScheduleEvent(EVENT_SPELL_HEAD_CRACK, 25000);
		}

		void AttackStart(Unit* who)
		{
			if( who->GetGUID() != TargetGUID )
				return;
			ScriptedAI::AttackStart(who);
		}

		void MoveInLineOfSight(Unit* /*who*/) {}

		void UpdateAI(uint32 diff)
		{
			if( !TargetGUID && !me->GetVehicle() )
				return;

			Unit* t = ObjectAccessor::GetUnit(*me, TargetGUID);
			if( !t && !(t = me->GetVehicleBase()) )
				return;

			if( t->isDead() )
			{
				t->RemoveAura(SPELL_CHANGE_VEHICLE);
				me->RemoveAllAuras();
				me->DeleteThreatList();
				me->CombatStop(true);
				me->SetHealth(me->GetMaxHealth());
				if( pInstance )
					if( Creature* gormok = ObjectAccessor::GetCreature(*me, pInstance->GetData64(TYPE_GORMOK)) )
						if( gormok->IsAlive() )
							if( Vehicle* vk = gormok->GetVehicleKit() )
								for( uint8 i=0; i<4; ++i )
									if( !vk->GetPassenger(i) )
									{
										me->EnterVehicleUnattackable(gormok, i);
										Reset();
										break;
									}
				TargetGUID = 0;
				return;
			}

			events.Update(diff);

			if( me->HasUnitState(UNIT_STATE_CASTING) )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_SNOBOLLED:
					if( t->GetTypeId() == TYPEID_PLAYER )
						me->CastSpell((Unit*)NULL, SPELL_SNOBOLLED, true);
					events.PopEvent();
					break;
				case EVENT_SPELL_BATTER:
					if( t->GetTypeId() == TYPEID_PLAYER )
						me->CastSpell(t, SPELL_BATTER);
					events.RepeatEvent(urand(6000,8000));
					break;
				case EVENT_SPELL_FIRE_BOMB:
					{
						if( t->GetTypeId() != TYPEID_PLAYER && pInstance )
						{
							std::vector<uint64> validPlayers;
							Map::PlayerList const &pl = me->GetMap()->GetPlayers();
							Creature* gormok = ObjectAccessor::GetCreature(*me, pInstance->GetData64(TYPE_GORMOK));

							for( Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr )
							{
								if( Player* p = itr->GetSource() )
									if( p->IsAlive() && p->GetGUID()!=TargetGUID && (!gormok || !p->IsWithinMeleeRange(gormok)) )
										validPlayers.push_back(p->GetGUID());
							}

							if( !validPlayers.empty() )
								if( Player* p = ObjectAccessor::GetPlayer(*me, validPlayers.at(urand(0,validPlayers.size()-1))) )
									if( Creature* trigger = me->SummonCreature(NPC_FIRE_BOMB, *p, TEMPSUMMON_TIMED_DESPAWN, 60000) )
									{
										me->CastSpell(trigger, SPELL_FIRE_BOMB_AURA, true); // periodic damage aura, speed 14.0f
										me->CastSpell(trigger, SPELL_FIRE_BOMB); // visual + initial damage 4k
									}
						}

						events.RepeatEvent(urand(20000,30000));
					}
					break;
				case EVENT_SPELL_HEAD_CRACK:
					if( t->GetTypeId() == TYPEID_PLAYER )
						me->CastSpell(t, SPELL_HEAD_CRACK);
					events.RepeatEvent(urand(30000,35000));
					break;
			}

			DoMeleeAttackIfReady();
		}

		void JustDied(Unit* /*pKiller*/)
		{
			if( Unit* t = ObjectAccessor::GetUnit(*me, TargetGUID))
			{
				t->RemoveAura(SPELL_CHANGE_VEHICLE);
				if( t->IsAlive() )
					t->RemoveAurasDueToSpell(SPELL_SNOBOLLED);
			}
		}

		void DoAction(int32 param)
		{
			if( param == 1 && !TargetGUID )
				me->DespawnOrUnsummon();
		}
	};
};

class boss_gormok : public CreatureScript
{
public:
	boss_gormok() : CreatureScript("boss_gormok") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new boss_gormokAI(pCreature);
	}

	struct boss_gormokAI : public ScriptedAI
	{
		boss_gormokAI(Creature* pCreature) : ScriptedAI(pCreature), summons(pCreature)
		{
			pInstance = pCreature->GetInstanceScript();
			me->AddUnitMovementFlag(MOVEMENTFLAG_WALKING);
			me->SetReactState(REACT_PASSIVE);
		}

		InstanceScript* pInstance;
		EventMap events;
		SummonList summons;
		uint64 PlayerGUID;

		void Reset()
		{
			events.Reset();
			summons.DespawnAll();
			PlayerGUID = 0;
		}

		void EnterCombat(Unit* /*who*/)
		{
			me->setActive(true);
			events.Reset();
			events.RescheduleEvent(EVENT_SPELL_IMPALE, urand(9000,10000));
			events.RescheduleEvent(EVENT_SPELL_STAGGERING_STOMP, 15000);
			events.RescheduleEvent(EVENT_PICK_SNOBOLD_TARGET, urand(16000,24000));

			// refresh snobold position
			if( Vehicle* vk = me->GetVehicleKit() )
				for( uint8 i=0; i<4; ++i )
					if( Unit* snobold = vk->GetPassenger(i) )
						snobold->SendMovementFlagUpdate();
		}

		void JustReachedHome()
		{
			me->setActive(false);
		}

		void MoveInLineOfSight(Unit* /*who*/) {}

		void UpdateAI(uint32 diff)
		{
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( me->HasUnitState(UNIT_STATE_CASTING) )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_IMPALE:
					if( !me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISARMED) )
					{
						if( Unit* victim = me->GetVictim() )
							me->CastSpell(victim, SPELL_IMPALE, false);
						events.RepeatEvent(urand(9000,10000));
					}
					else
						events.RepeatEvent(2500);
					break;
				case EVENT_SPELL_STAGGERING_STOMP:
					me->CastSpell((Unit*)NULL, SPELL_STAGGERING_STOMP, false);
					events.RepeatEvent(urand(20000,25000));
					break;
				case EVENT_PICK_SNOBOLD_TARGET:
					if( Vehicle* vk = me->GetVehicleKit() )
						for( uint8 i=0; i<4; ++i )
							if( Unit* snobold = vk->GetPassenger(i) )
							{
								std::vector<uint64> validPlayers;
								Map::PlayerList const &pl = me->GetMap()->GetPlayers();
								for( Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr )
								{
									if( Player* p = itr->GetSource() )
										if( p->IsAlive() && !p->GetVehicleKit() && !p->IsMounted() && !p->GetVehicle() && !p->IsGameMaster() )
											validPlayers.push_back(p->GetGUID());
								}

								if( !validPlayers.empty() )
									if( Player* p = ObjectAccessor::GetPlayer(*me, validPlayers.at(urand(0,validPlayers.size()-1))) )
									{
										snobold->ChangeSeat(4); // switch to hand
										me->setAttackTimer(BASE_ATTACK, 3000);
										PlayerGUID = p->GetGUID();
										events.RescheduleEvent(EVENT_RELEASE_SNOBOLD, 2500);
									}

								break;
							}
					events.RepeatEvent(urand(16000,24000));
					break;
				case EVENT_RELEASE_SNOBOLD:
					{
						me->CastSpell(me, SPELL_RISING_ANGER, true);
						Player* p = ObjectAccessor::GetPlayer(*me, PlayerGUID);
						if( p && p->IsAlive() && !p->GetVehicleKit() && !p->IsMounted() && !p->GetVehicle() )
						{
							if( Vehicle* vk = me->GetVehicleKit() )
								if( Unit* snobold = vk->GetPassenger(4) )
								{
									if( snobold->GetTypeId() == TYPEID_UNIT )
									{
										CAST_AI(npc_snobold_vassal::npc_snobold_vassalAI, snobold->ToCreature()->AI())->TargetGUID = PlayerGUID;
										snobold->ToCreature()->AI()->AttackStart(p);
									}
									//Talk(EMOTE_SNOBOLLED);
									p->CastSpell(p, SPELL_CHANGE_VEHICLE, true);
									snobold->EnterVehicle(p, 0);
									//snobold->ClearUnitState(UNIT_STATE_ONVEHICLE);
								}
						}
						else if( Vehicle* vk = me->GetVehicleKit() )
						{
							events.RescheduleEvent(EVENT_PICK_SNOBOLD_TARGET, 5000); // player not found (died? left instance?), pick new one faster!
							if( Unit* snobold = vk->GetPassenger(4) )
								if( snobold->GetTypeId() == TYPEID_UNIT )
								{
									bool needDespawn = true;
									for( uint8 i=0; i<4; ++i )
										if( !vk->GetPassenger(i) )
										{
											snobold->ChangeSeat(i);
											needDespawn = false;
											break;
										}
									if( needDespawn )
										snobold->ToCreature()->DespawnOrUnsummon();
								}
						}
						PlayerGUID = 0;
						events.PopEvent();
					}
					break;
			}

			DoMeleeAttackIfReady();
		}

		void JustDied(Unit* /*pKiller*/)
		{
			summons.DoAction(1);

			if( pInstance )
				pInstance->SetData(TYPE_GORMOK, DONE);
		}

		void JustSummoned(Creature* summon)
		{
			summons.Summon(summon);
		}

		void DoAction(int32 param)
		{
			switch( param )
			{
				case -1:
					summons.DespawnAll();
					break;
			}
		}

		void EnterEvadeMode()
		{
			events.Reset();
			summons.DespawnAll();
			me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
			if( pInstance )
				pInstance->SetData(TYPE_FAILED, 1);
		}
	};
};


/***********
** ACIDMAW AND DREADSCALE
***********/

enum JormungarSpells
{
	SPELL_ACID_SPIT						= 66880,
	SPELL_ACID_SPEW						= 66818,
	SPELL_PARALYTIC_SPRAY				= 66901,
	SPELL_PARALYTIC_BITE				= 66824,

	SPELL_FIRE_SPIT						= 66796,
	SPELL_MOLTEN_SPEW					= 66821,
	SPELL_BURNING_SPRAY					= 66902,
	SPELL_BURNING_BITE					= 66879,

	SUMMON_SLIME_POOL					= 66883,
	SPELL_SLIME_POOL_EFFECT				= 66882,
	SPELL_SWEEP_0						= 66794,
	SPELL_SWEEP_1						= 67646,

	SPELL_EMERGE_0						= 66947,
	SPELL_SUBMERGE_0					= 53421,
	SPELL_ENRAGE						= 68335,
	SPELL_CHURNING_GROUND				= 66969,
};

enum Model
{
	MODEL_ACIDMAW_STATIONARY			= 29815,
	MODEL_ACIDMAW_MOBILE				= 29816,
	MODEL_DREADSCALE_STATIONARY			= 26935,
	MODEL_DREADSCALE_MOBILE				= 24564,
};

enum JormungarNPCs
{
	NPC_SLIME_POOL						= 35176,
};

enum JormungarEvents
{
	EVENT_SUBMERGE = 1,
	EVENT_EMERGE,
	EVENT_MOVE_UNDERGROUND,

	EVENT_SPELL_SPRAY,
	EVENT_SPELL_SWEEP,
	EVENT_SPELL_BITE,
	EVENT_SPELL_SPEW,
	EVENT_SPELL_SLIME_POOL,
};

struct boss_jormungarAI : public ScriptedAI
{
	boss_jormungarAI(Creature* pCreature) : ScriptedAI(pCreature)
	{
		pInstance = pCreature->GetInstanceScript();
		me->SetReactState(REACT_PASSIVE);
	}

	InstanceScript* pInstance;
	EventMap events;
	bool bIsStationary;

	uint32 _SPELL_BITE;
	uint32 _SPELL_SPEW;
	uint32 _SPELL_SPIT;
	uint32 _SPELL_SPRAY;
	uint32 _MODEL_STATIONARY;
	uint32 _MODEL_MOBILE;
	uint32 _TYPE_OTHER;

	void DoAction(int32 param)
	{
		switch( param )
		{
			case -1:
				if( !me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE) )
					events.RescheduleEvent(EVENT_SUBMERGE, 1500);
				break;
			case -2:
				if( me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE) )
					bIsStationary = true; // it will come out mobile soon
				else if( me->GetDisplayId() == _MODEL_STATIONARY )
					events.RescheduleEvent(EVENT_SUBMERGE, 1000);
				else
					events.CancelEvent(EVENT_SUBMERGE);
				me->CastSpell(me, SPELL_ENRAGE, true);
				Talk(EMOTE_ENRAGE);
				break;
		}
	}

	void ScheduleEvents()
	{
		events.Reset();
		if( me->GetDisplayId() == _MODEL_STATIONARY )
		{
			me->SetAttackTime(BASE_ATTACK, 1500);
			events.RescheduleEvent(EVENT_SPELL_SPRAY, (me->GetEntry() == NPC_ACIDMAW ? 20000 : 15000));
			events.RescheduleEvent(EVENT_SPELL_SWEEP, urand(15000,30000));
		}
		else
		{
			me->SetAttackTime(BASE_ATTACK, 2000);
			events.RescheduleEvent(EVENT_SPELL_BITE, (me->GetEntry() == NPC_ACIDMAW ? 20000 : 15000));
			events.RescheduleEvent(EVENT_SPELL_SPEW, urand(15000,30000));
			events.RescheduleEvent(EVENT_SPELL_SLIME_POOL, 15000);
		}
		if( !me->HasAura(SPELL_ENRAGE) )
			events.RescheduleEvent(EVENT_SUBMERGE, urand(45000,50000));
	}

	void EnterCombat(Unit* /*who*/)
	{
		me->setActive(true);
		ScheduleEvents();
	}

	void JustReachedHome()
	{
		me->setActive(false);
	}

	void AttackStart(Unit* who)
	{
		if( me->GetDisplayId() == _MODEL_STATIONARY )
		{
			if( !who )
				return;
			if( me->Attack(who, true) )
				DoStartNoMovement(who);
		}
		else
			ScriptedAI::AttackStart(who);
	}

	void UpdateAI(uint32 diff)
	{
		if( !UpdateVictim() )
			return;

		events.Update(diff);

		if( me->HasUnitState(UNIT_STATE_CASTING) )
			return;

		switch( events.GetEvent() )
		{
			case 0:
				break;
			case EVENT_SUBMERGE:
				{
					bIsStationary = me->GetDisplayId() == _MODEL_STATIONARY ? true : false;
					me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
					me->CastSpell(me, SPELL_SUBMERGE_0, false);
					Talk(EMOTE_SUBMERGE);

					// second one submerge 1.5sec after the first one, used also for synchronizing
					if( pInstance )
						if( Creature* c = ObjectAccessor::GetCreature(*me, pInstance->GetData64(_TYPE_OTHER)) )
							c->AI()->DoAction(-1);

					events.Reset();
					events.RescheduleEvent(EVENT_MOVE_UNDERGROUND, 2500);
				}
				break;
			case EVENT_MOVE_UNDERGROUND:
				{
					float angle=me->GetAngle(Locs[LOC_CENTER].GetPositionX()+urand(0,20)-10.0f, Locs[LOC_CENTER].GetPositionY()+urand(0,20)-10.0f), dist=urand(10,35);
					if( Creature* c = me->SummonCreature(NPC_WORLD_TRIGGER, *me, TEMPSUMMON_TIMED_DESPAWN, 6000) )
					{
						c->SetSpeed(MOVE_RUN, 2.5f);
						c->CastSpell(c, SPELL_CHURNING_GROUND, true);
						c->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED);
						c->GetMotionMaster()->MovePoint(0, Locs[LOC_CENTER].GetPositionX()+cos(angle)*dist, Locs[LOC_CENTER].GetPositionY()+sin(angle)*dist, me->GetPositionZ());
					}
					me->UpdatePosition(Locs[LOC_CENTER].GetPositionX()+cos(angle)*dist, Locs[LOC_CENTER].GetPositionY()+sin(angle)*dist, me->GetPositionZ(), me->GetOrientation(), true);
					me->StopMovingOnCurrentPos();
					DoResetThreat();
					events.PopEvent();
					events.RescheduleEvent(EVENT_EMERGE, 6000);
				}
				break;
			case EVENT_EMERGE:
				{
					me->GetMotionMaster()->Clear();
					me->GetMotionMaster()->MoveIdle();
					me->StopMoving();
					if( bIsStationary )
					{
						me->SetNativeDisplayId(_MODEL_MOBILE);
						SetCombatMovement(true);
						if( Unit* victim = me->GetVictim() )
							me->GetMotionMaster()->MoveChase(victim);
					}
					else
					{
						me->SetNativeDisplayId(_MODEL_STATIONARY);
						SetCombatMovement(false);
					}
					me->RemoveAurasDueToSpell(SPELL_SUBMERGE_0);
					me->CastSpell(me, SPELL_EMERGE_0, false);
					Talk(EMOTE_EMERGE);
					me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
					ScheduleEvents();
				}
				break;
			case EVENT_SPELL_SPRAY:
				if( Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true) )
					me->CastSpell(target, _SPELL_SPRAY, false);
				events.RepeatEvent(20000);
				break;
			case EVENT_SPELL_SWEEP:
				me->CastSpell((Unit*)NULL, SPELL_SWEEP_0, false);
				events.RepeatEvent(urand(15000,30000));
				break;
			case EVENT_SPELL_BITE:
				if( Unit* victim = me->GetVictim() )
					me->CastSpell(victim, _SPELL_BITE, false);
				events.RepeatEvent(20000);
				break;
			case EVENT_SPELL_SPEW:
				me->CastSpell(me->GetVictim(), _SPELL_SPEW, false);
				events.RepeatEvent(urand(15000,30000));
				break;
			case EVENT_SPELL_SLIME_POOL:
				if( Creature* c = me->SummonCreature(NPC_SLIME_POOL, *me, TEMPSUMMON_TIMED_DESPAWN, 30000) )
					c->CastSpell(c, SPELL_SLIME_POOL_EFFECT, true);
				events.RepeatEvent(30000);
				break;
		}

		if( !me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE) )
		{
			if( me->GetDisplayId() == _MODEL_STATIONARY )
				DoSpellAttackIfReady(_SPELL_SPIT);
			else
				DoMeleeAttackIfReady();
		}
	}

	void JustDied(Unit* /*pKiller*/)
	{
		if( pInstance )
		{
			if( Creature* c = pInstance->instance->GetCreature(pInstance->GetData64(_TYPE_OTHER)) )
				if( c->IsAlive() )
					c->AI()->DoAction(-2);
			pInstance->SetData(TYPE_JORMUNGAR, DONE);
		}
	}

	void EnterEvadeMode()
	{
		events.Reset();
		me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
		if( pInstance )
			pInstance->SetData(TYPE_FAILED, 1);
	}
};

class boss_acidmaw : public CreatureScript
{
	public:
	boss_acidmaw() : CreatureScript("boss_acidmaw") { }

	struct boss_acidmawAI : public boss_jormungarAI
	{
		boss_acidmawAI(Creature* pCreature) : boss_jormungarAI(pCreature)
		{
			_SPELL_BITE = SPELL_PARALYTIC_BITE;
			_SPELL_SPEW = SPELL_ACID_SPEW;
			_SPELL_SPIT = SPELL_ACID_SPIT;
			_SPELL_SPRAY = SPELL_PARALYTIC_SPRAY;
			_MODEL_STATIONARY = MODEL_ACIDMAW_STATIONARY;
			_MODEL_MOBILE = MODEL_ACIDMAW_MOBILE;
			_TYPE_OTHER = TYPE_DREADSCALE;
			SetCombatMovement(false);
		}
	};


	CreatureAI* GetAI(Creature* creature) const
	{
		return new boss_acidmawAI(creature);
	}
};

class boss_dreadscale : public CreatureScript
{
public:
	boss_dreadscale() : CreatureScript("boss_dreadscale") { }

	struct boss_dreadscaleAI : public boss_jormungarAI
	{
		boss_dreadscaleAI(Creature* pCreature) : boss_jormungarAI(pCreature)
		{
			_SPELL_BITE = SPELL_BURNING_BITE;
			_SPELL_SPEW = SPELL_MOLTEN_SPEW;
			_SPELL_SPIT = SPELL_FIRE_SPIT;
			_SPELL_SPRAY = SPELL_BURNING_SPRAY;
			_MODEL_STATIONARY = MODEL_DREADSCALE_STATIONARY;
			_MODEL_MOBILE = MODEL_DREADSCALE_MOBILE;
			_TYPE_OTHER = TYPE_ACIDMAW;
		}
	};

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new boss_dreadscaleAI(pCreature);
	}
};


/***********
** ICEHOWL
***********/

enum IcehowlSpells
{
	SPELL_FEROCIOUS_BUTT				= 66770,
	SPELL_WHIRL							= 67345,
	SPELL_ARCTIC_BREATH					= 66689,

	SPELL_MASSIVE_CRASH					= 66683,
	SPELL_JUMP_BACK						= 66733,
	SPELL_TRAMPLE						= 66734,
	SPELL_FROTHING_RAGE					= 66759,
	SPELL_STAGGERED_DAZE				= 66758,
	SPELL_BERSERK						= 26662,
	SPELL_SURGE_OF_ADRENALINE			= 68667,
};

enum IcehowlEvents
{
	EVENT_JUMP_MIDDLE = 1,
	EVENT_GAZE,
	EVENT_JUMP_BACK,
	EVENT_TRAMPLE,
	EVENT_CHECK_TRAMPLE_PLAYERS,
	EVENT_REFRESH_POSITION,
	EVENT_SPELL_FEROCIOUS_BUTT,
	EVENT_SPELL_MASSIVE_CRASH,
	EVENT_SPELL_WHIRL,
	EVENT_SPELL_ARCTIC_BREATH,
};

class boss_icehowl : public CreatureScript
{
public:
	boss_icehowl() : CreatureScript("boss_icehowl") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new boss_icehowlAI(pCreature);
	}

	struct boss_icehowlAI : public ScriptedAI
	{
		boss_icehowlAI(Creature* pCreature) : ScriptedAI(pCreature)
		{
			pInstance = pCreature->GetInstanceScript();
			me->AddUnitMovementFlag(MOVEMENTFLAG_WALKING);
			me->SetReactState(REACT_PASSIVE);
			if (IsHeroic())
				me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_DISPEL, true);
			me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_DECREASE_SPEED, true);
			me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_SPEED_NOT_STACK, true);
			me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_SPEED_ALWAYS, true);
			me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_SPEED_SLOW_ALL, true);
			me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED, true); // judgement of justice
			//me->SetLootMode(0); // [LOOT]
		}

		InstanceScript* pInstance;
		EventMap events;
		uint64 TargetGUID;
		float destX, destY, destZ;

		void AttackStart(Unit* who)
		{
			if (me->GetReactState() != REACT_PASSIVE)
				ScriptedAI::AttackStart(who);
		}

		void EnterCombat(Unit* /*who*/)
		{
			me->setActive(true);
			events.Reset();
			events.ScheduleEvent(EVENT_SPELL_FEROCIOUS_BUTT, urand(15000,30000));
			events.RescheduleEvent(EVENT_SPELL_WHIRL, urand(10000,12000));
			events.RescheduleEvent(EVENT_SPELL_ARCTIC_BREATH, 14000);
			events.RescheduleEvent(EVENT_JUMP_MIDDLE, 30000);
		}

		void JustReachedHome()
		{
			me->setActive(false);
		}

		bool DoTrampleIfValid()
		{
			Map::PlayerList const &lPlayers = me->GetMap()->GetPlayers();
			for( Map::PlayerList::const_iterator itr = lPlayers.begin(); itr != lPlayers.end(); ++itr )
				if( Unit* p = itr->GetSource() )
					if( p->IsAlive() && p->GetExactDist(me) <= 12.0f )
					{
						DoCastAOE(SPELL_TRAMPLE);
						return true;
					}

			return false;
		}

		void MovementInform(uint32 type, uint32 id)
		{
			if( id == EVENT_CHARGE )
			{
				events.Reset();
				events.RescheduleEvent(EVENT_SPELL_FEROCIOUS_BUTT, urand(5000,15000));
				events.RescheduleEvent(EVENT_SPELL_WHIRL, urand(2000,5000));
				events.RescheduleEvent(EVENT_SPELL_ARCTIC_BREATH, urand(5000,8000));
				events.RescheduleEvent(EVENT_JUMP_MIDDLE, urand(30000,50000));

				float angle = me->GetAngle(&Locs[LOC_CENTER]);
				angle = angle >= M_PI ? angle-M_PI : angle+M_PI;

				me->UpdatePosition(destX, destY, destZ, angle, true);
				me->StopMovingOnCurrentPos();

				if( !DoTrampleIfValid() )
				{
					me->CastSpell(me, SPELL_STAGGERED_DAZE, true);
					me->CastSpell((Unit*)NULL, SPELL_TRAMPLE, true);
					Talk(EMOTE_TRAMPLE_CRASH);
					events.DelayEvents(15000);
				}
				else
				{
					Talk(EMOTE_TRAMPLE_FAIL);
					me->CastSpell(me, SPELL_FROTHING_RAGE, true);
				}

				me->SetReactState(REACT_AGGRESSIVE);
			}
		}

		void UpdateAI(uint32 diff)
		{
			if( !UpdateVictim() )
				return;

			events.Update(diff);

			if( me->HasUnitState(UNIT_STATE_CASTING) )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_SPELL_FEROCIOUS_BUTT:
					if( Unit* victim = me->GetVictim() )
						me->CastSpell(victim, SPELL_FEROCIOUS_BUTT, false);
					events.RepeatEvent(urand(15000,30000));
					break;
				case EVENT_SPELL_WHIRL:
					me->CastSpell((Unit*)NULL, SPELL_WHIRL, false);
					events.RepeatEvent(urand(15000,20000));
					break;
				case EVENT_SPELL_ARCTIC_BREATH:
					if( Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 90.0f, true) )
						me->CastSpell(target, SPELL_ARCTIC_BREATH, false);
					events.RepeatEvent(urand(20000,30000));
					break;
				case EVENT_JUMP_MIDDLE:
					me->StopMoving();
					me->GetMotionMaster()->Clear();
					me->GetMotionMaster()->MoveIdle();
					me->SetReactState(REACT_PASSIVE);
					me->AttackStop();
					me->GetMotionMaster()->MoveJump(Locs[LOC_CENTER].GetPositionX(), Locs[LOC_CENTER].GetPositionY(), Locs[LOC_CENTER].GetPositionZ(), 40.0f, 12.0f);
					me->SetUInt64Value(UNIT_FIELD_TARGET, 0);
					events.Reset();
					events.RescheduleEvent(EVENT_SPELL_MASSIVE_CRASH, 2000);
					break;
				case EVENT_SPELL_MASSIVE_CRASH:
					me->GetMotionMaster()->Clear();
					me->CastSpell((Unit*)NULL, SPELL_MASSIVE_CRASH, false);
					events.PopEvent();
					events.RescheduleEvent(EVENT_GAZE, 2000);
					break;
				case EVENT_GAZE:
					if( Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 500.0f, true) )
					{
						TargetGUID = target->GetGUID();
						me->SetUInt64Value(UNIT_FIELD_TARGET, TargetGUID);
						me->SetFacingToObject(target);
						Talk(EMOTE_TRAMPLE_STARE, target);
						me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);
						events.RescheduleEvent(EVENT_JUMP_BACK, 2000);
					}
					else // in case something went wrong
					{
						events.RescheduleEvent(EVENT_SPELL_FEROCIOUS_BUTT, urand(5000,15000));
						events.RescheduleEvent(EVENT_SPELL_WHIRL, urand(2000,5000));
						events.RescheduleEvent(EVENT_SPELL_ARCTIC_BREATH, urand(5000,8000));
						events.RescheduleEvent(EVENT_JUMP_MIDDLE, urand(30000,50000));
						me->GetMotionMaster()->MovementExpired();
						me->SetReactState(REACT_AGGRESSIVE);
					}
					events.PopEvent();
					break;
				case EVENT_JUMP_BACK:
					{
						float angle;
						if( Unit* target = ObjectAccessor::GetPlayer(*me, TargetGUID) )
							angle = me->GetAngle(target);
						else // in case something went wrong
							angle = rand_norm()*2*M_PI;

						float jumpangle = angle >= M_PI ? angle-M_PI : angle+M_PI;
						float dist = 50.0f;
						if( angle > 1.0f && angle < 2.0f ) // near main gate
							dist = 46.0f;
						destX = Locs[LOC_CENTER].GetPositionX()+cos(angle)*dist;
						destY = Locs[LOC_CENTER].GetPositionY()+sin(angle)*dist;
						destZ = Locs[LOC_CENTER].GetPositionZ()+1.0f;
						me->StopMoving();
						me->GetMotionMaster()->MoveJump(Locs[LOC_CENTER].GetPositionX()+cos(jumpangle)*35.0f, Locs[LOC_CENTER].GetPositionY()+sin(jumpangle)*35.0f, Locs[LOC_CENTER].GetPositionZ()+1.0f, 40.0f, 12.0f);
						
						events.PopEvent();
						events.RescheduleEvent(EVENT_TRAMPLE, 1500);

						if( pInstance )
							switch( GetDifficulty() )
							{
								case RAID_DIFFICULTY_10MAN_NORMAL:
									pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_MASSIVE_CRASH);
									pInstance->DoCastSpellOnPlayers(SPELL_SURGE_OF_ADRENALINE);
									break;
								case RAID_DIFFICULTY_25MAN_NORMAL:
									pInstance->DoRemoveAurasDueToSpellOnPlayers(67660);
									pInstance->DoCastSpellOnPlayers(SPELL_SURGE_OF_ADRENALINE);
									break;
								case RAID_DIFFICULTY_10MAN_HEROIC:
									pInstance->DoRemoveAurasDueToSpellOnPlayers(67661);
									break;
								case RAID_DIFFICULTY_25MAN_HEROIC:
									pInstance->DoRemoveAurasDueToSpellOnPlayers(67662);
									break;
							}
					}
					break;
				case EVENT_TRAMPLE:
					//Talk(EMOTE_TRAMPLE_START);
					me->DisableSpline();
					me->GetMotionMaster()->Clear();
					me->GetMotionMaster()->MoveCharge(destX, destY, destZ+1.0f, 65.0f);
					me->SetUInt64Value(UNIT_FIELD_TARGET, 0);
					events.RescheduleEvent(EVENT_CHECK_TRAMPLE_PLAYERS, 100);
					events.PopEvent();
					break;
				case EVENT_CHECK_TRAMPLE_PLAYERS:
					if( DoTrampleIfValid() )
					{
						events.Reset();
						events.RescheduleEvent(EVENT_SPELL_FEROCIOUS_BUTT, urand(5000,15000));
						events.RescheduleEvent(EVENT_SPELL_WHIRL, urand(2000,5000));
						events.RescheduleEvent(EVENT_SPELL_ARCTIC_BREATH, urand(5000,8000));
						events.RescheduleEvent(EVENT_JUMP_MIDDLE, urand(30000,50000));
						Talk(EMOTE_TRAMPLE_FAIL);
						me->CastSpell(me, SPELL_FROTHING_RAGE, true);
						me->GetMotionMaster()->MovementExpired();
						me->SetReactState(REACT_AGGRESSIVE);
					}
					// no PopEvent() intended!
					break;
				case EVENT_REFRESH_POSITION:
					//me->SetFacingTo(me->GetOrientation());
					events.PopEvent();
					break;
			}

			if( me->GetReactState() != REACT_PASSIVE )
				DoMeleeAttackIfReady();
		}

		void EnterEvadeMode()
		{
			events.Reset();
			me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
			if( pInstance )
				pInstance->SetData(TYPE_FAILED, 1);
		}

		void JustDied(Unit* /*killer*/)
		{
			if( !pInstance )
				return;

			pInstance->SetData(TYPE_ICEHOWL, DONE);

			Player* plr = NULL;
			if( !pInstance->instance->GetPlayers().isEmpty() )
				plr = pInstance->instance->GetPlayers().begin()->GetSource();

			if( !plr )
				return;

			// remove loot for the other faction (items are invisible for players, done in conditions), so corpse can be skinned
			for( std::vector<LootItem>::iterator itr = me->loot.items.begin(); itr != me->loot.items.end(); ++itr )
				if( ItemTemplate const *iProto = sObjectMgr->GetItemTemplate((*itr).itemid) )
					if( ((iProto->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY) && plr->GetTeamId() != TEAM_HORDE) || ((iProto->Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY) && plr->GetTeamId() != TEAM_ALLIANCE) )
					{
						(*itr).count = 0;
						(*itr).is_looted = true;
						--me->loot.unlootedCount;
					}
		}
	};
};


void AddSC_boss_northrend_beasts()
{
	new boss_gormok();
	new npc_snobold_vassal();

	new boss_acidmaw();
	new boss_dreadscale();

	new boss_icehowl();
}
